#ifndef _TYPESYSTEM_H
#define _TYPESYSTEM_H

#include "AST.h"

#include <string>
#include <map>
#include <vector>

using std::string;
using namespace llvm;

using TypeNamePair = std::pair<std::string, std::string>;
enum Types
{
  type_invalid = 0,
  type_int = 1,
  type_double = 2,
  type_char = 3,
  type_bool = 4,
  type_void = 5,
  type_string = 6,
  type_struct = 7
};

class TypeSystem
{
private:
  LLVMContext &llvmContext;

  std::map<string, std::vector<TypeNamePair>> structMembers;

  std::map<string, llvm::StructType *> structTypes;

  std::map<Type *, std::map<Type *, CastInst::CastOps>> _castTable;

  void addCast(Type *from, Type *to, CastInst::CastOps op);

  Value *cast(Value *value, Type *type, BasicBlock *block);

public:
  Type *intTy = Type::getInt32Ty(llvmContext);
  Type *charTy = Type::getInt8Ty(llvmContext);
  Type *doubleTy = Type::getDoubleTy(llvmContext);
  Type *stringTy = Type::getInt8PtrTy(llvmContext);
  Type *voidTy = Type::getVoidTy(llvmContext);
  Type *boolTy = Type::getInt1Ty(llvmContext);

  TypeSystem(LLVMContext &context);

  void addStructType(string structName, llvm::StructType *);

  void addStructMember(string structName, string memType, string memName);

  long getStructMemberIndex(string structName, string memberName);

  Type *getVarType(string typeStr);

  Value *getDefaultValue(string typeStr);

  
  Value *cast(Value *value, std::string typeStr, BasicBlock *block);

  bool isStruct(string typeStr) const;

  static string llvmTypeToStr(Value *value);
  static string llvmTypeToStr(Type *type);
  std::string getStructMemberType(string structName, string memberName);
};
Value *CastToBoolean(Value *cond);

#endif //TINYCOMPILER_TYPESYSTEM_H